package com.pic.mgt.monitor;
import java.io.File;
import java.io.IOException;
import java.util.List;

import org.apache.commons.io.FileUtils;

import com.pic.mgt.file.io.FileOperation;
import com.pic.mgt.log.IgxLog;
import com.pic.mgt.monitor.backup.UnitBackup;
import com.pic.mgt.monitor.parser.FormatParser;
import com.pic.mgt.unit.Business;
import com.pic.mgt.unit.Unit;

public abstract class BusinessUnitConverter extends UnitConverter {
	protected File targetFile;
	private FormatParser businessSerialNumberParser;
	private FormatParser businessTargetFileParser;
	
	private Business business;
	private List<String> confirmationFileContent;
	
	private UnitBackup businessUnitBackup;
	
	private FileOperation fileOperation;
	
	private boolean isDoBizConfirmationFileRedundantCheck;
	
	@Override	
	public void parseUnitTypeContent(Unit unit) 
									throws IOException,
										    WrongUnitFormatException,
										    RedundantConfirmationFileException {
		this.business = (Business)unit;
		
		/*
		 *  先把 Confirmation File 內容讀出至 List當中以便讓 subclass 可以取用
		 */
		// throws IOException
		this.confirmationFileContent = 	
							getCFMTFileContent();
		
		// 分析並填入單元型態的內容(第二層級)
		try {
			doFill();
		} catch (NoTargetFileException e) {
			/*
			 *  若是屬於 target file 不存在的 Exception 則需要再做判斷.
			 *  (其它的WrongUnitFormatException則往上一層拋)
			 */
			
			if (this.isDoBizConfirmationFileRedundantCheck) {
				/*
				 * 因為PP使用3G/GPRS上傳檔案不穩定,很有可能會上傳檔案PP認為失敗,但實際上
				 * 確認檔已經正確產生出來,而且已經被系統處理掉。因為PP認為上傳失敗，所以
				 * 會 retry 重上傳確認檔，導致多一個確認檔。
				 * 所以程式提供設定，可以去判斷如果確認檔沒有對應的target file，則會先去
				 * Backup目錄底下看，是否已經有該業務存在了，如果有則不用拋Error。
				 */
				File bizBackupPath = this.businessUnitBackup.getBackupUnitPath();
				File backupConfirmationFile = new File(bizBackupPath.getAbsolutePath(),
													   unit.getConfirmationFilename());
				if (backupConfirmationFile.exists()) {
					try {
						this.fileOperation.deleteFile(unit.getConfirmationFile());
						
						// ********************** Log **********************
						IgxLog.messageLog.warn(this,
										"An redundant confirmation file '{}' of business has been ignored and removed.",
										unit.getConfirmationFile().getAbsolutePath());
						// ********************** Log **********************
						
						throw new RedundantConfirmationFileException(unit.getConfirmationFile().getAbsolutePath());
					} catch (IOException e1) {
						// ********************** Log **********************
						IgxLog.messageDetailsLog.error(this,
										"Removing redundant confirmation file '{}' of business failed.",
										unit.getConfirmationFile().getAbsolutePath());
						// ********************** Log **********************
						
						throw e1;
					}
				}
				else {
					throw e;
				}
			}
			else {
				throw e;
			}
		}
		
		// 分析並填入業務單元型態內容 (第三層級)
		parseIGXTypeContent(this.business);
	}
	
	/*
	 * 放入單元型態內容資訊:
	 * (1) Business Serial Number
	 * (2) 確認檔位置
	 * (3) 目標檔位置
	 * (4) Target File Size
	 */
	private void doFill() throws WrongUnitFormatException {
		// --------------------- (1) Business Serial Number ------------
		// throws WrongUnitFormatException
		fillBusinessSerialNumber();
		
		// --------------------- (2) 確認檔位置 -------------------------
		fillConfirmationFile();
		
		// --------------------- (3) 目標檔位置 -------------------------
		fillTargetFile();
		
		// (送多方新增)
		// --------------------- (4) Target File Size -------------------------
		fillTargetFileSize();
	}
	
	private void fillBusinessSerialNumber() throws WrongUnitFormatException {
		String businessSN = (String)
								this.businessSerialNumberParser.
									parse(super.confirmationFile.getName());
		this.business.setBusinessSN(businessSN);
	}
	
	private void fillConfirmationFile() {
		this.business.setConfirmationFile(super.confirmationFile);		
	}
	
	private void fillTargetFile() throws WrongUnitFormatException {
		this.targetFile = (File)
			this.businessTargetFileParser.parse(super.confirmationFile);
		
		this.business.setTargetFile(this.targetFile);
	}
	
	// (送多方新增)
	private void fillTargetFileSize() {
		this.business.setTargetFileSize(this.targetFile.length());
	}
	
	/**
	 * 讀取確認檔內容,並將內容轉換成為List
	 * (以行為區隔)
	 * @return
	 * @throws IOException
	 */
	private List<String> getCFMTFileContent() throws IOException {		
		@SuppressWarnings(value={"unchecked"})
		// 直接使用 Apache Common IO 套件來達成
		// throw IOException
		List<String> contentList = 
						FileUtils.readLines(super.confirmationFile);	
		return contentList;
	}
	
	/**
	 * 取得 確認檔內容的第 n 行
	 * @param lineNo
	 * @return
	 * @throws IndexOutOfBoundsException
	 */
	protected String getConfirmationContentAtLine(int lineNo)
									throws IndexOutOfBoundsException{
		return
				this.confirmationFileContent.get(lineNo);		
	}
	
	/**
	 * 取得確認檔內容從第n行,到最後一行
	 * @param fromLineNo
	 * @return
	 * @throws IndexOutOfBoundsException
	 */
	protected String[] getConfirmationContentFromLineToEnd(int fromLineNo)
										throws IllegalArgumentException{
		int listSize = this.confirmationFileContent.size();
		List<String> subConfirmationFileContentList = 
				this.confirmationFileContent.subList(fromLineNo, listSize);
		
		return
			subConfirmationFileContentList.toArray
							(new String[listSize - fromLineNo]);
	}
	
	protected boolean checkTargetFilesizeMatched(long filesize) {
		boolean matched;
		if (this.targetFile.length() == filesize) {
			matched = true;
		}
		else {
			matched = false;
		}
		return matched;
	}
	
	protected abstract void parseIGXTypeContent(Business business)
					throws WrongUnitFormatException;
	
	// --- setter and getter ---

	public void setBusinessSerialNumberParser(
									FormatParser businessSerialNumberParser) {
		this.businessSerialNumberParser = businessSerialNumberParser;
	}

	public void setBusinessTargetFileParser(FormatParser businessTargetFileParser) {
		this.businessTargetFileParser = businessTargetFileParser;
	}

	public void setDoBizConfirmationFileRedundantCheck(
			boolean isDoBizConfirmationFileRedundantCheck) {
		this.isDoBizConfirmationFileRedundantCheck = isDoBizConfirmationFileRedundantCheck;
	}

	public void setBusinessUnitBackup(UnitBackup businessUnitBackup) {
		this.businessUnitBackup = businessUnitBackup;
	}

	public void setFileOperation(FileOperation fileOperation) {
		this.fileOperation = fileOperation;
	}
}
